﻿using System;
using System.Collections.Concurrent;
using System.Threading;

namespace TC.Vacation.Spider.Web
{
    /// <summary>
    /// 异步请求管理器
    /// </summary>
    internal static class AsyncWebRequestManager
    {
        #region 私有属性

        /// <summary>
        /// The last asynchronous request identifier
        /// </summary>
        private static int LastAsyncRequestID = 0;

        /// <summary>
        /// Gets the asynchronous events.
        /// </summary>
        /// <value>
        /// The asynchronous events.
        /// </value>
        private static ConcurrentBag<ManualResetEvent> AsyncEvents
        {
            get
            {
                return _AsyncEvents.Value;
            }
        }
        /// <summary>
        /// The _ asynchronous events
        /// </summary>
        private static Lazy<ConcurrentBag<ManualResetEvent>> _AsyncEvents = new Lazy<ConcurrentBag<ManualResetEvent>>();

        #endregion

        #region 公开方法

        /// <summary>
        /// 开始一个异步请求
        /// </summary>
        /// <param name="url">请求的链接</param>
        /// <param name="success">请求成功后的回调函数</param>
        /// <param name="fail">请求失败后的回调函数</param>
        /// <param name="options">选项</param>
        /// <returns>一个唯一标识ID</returns>

        public static int StartAsyncWebRequest(string url, Action<ICsqWebResponse> success, Action<ICsqWebResponse> fail, ServerConfig options = null)
        {
            int id = GetAsyncRequestID();
            StartAsyncWebRequest(url, success, fail, id, options);
            return id;
        }

        /// <summary>
        /// 开始一个异步请求
        /// </summary>
        /// <param name="request">请求接口</param>
        /// <param name="success">请求成功后的回调函数</param>
        /// <param name="fail">请求失败后的回调函数</param>
        public static void StartAsyncWebRequest(ICsqWebRequest request, Action<ICsqWebResponse> success, Action<ICsqWebResponse> fail)
        {
            var requestObj = (CsqWebRequest)request;
            requestObj.Async = true;

            var mrEvent = requestObj.GetAsync(success, fail);
            AsyncEvents.Add(mrEvent);
        }

        /// <summary>
        /// 开始一个异步请求
        /// </summary>
        /// <param name="url">请求的链接</param>
        /// <param name="success">请求成功后的回调函数</param>
        /// <param name="fail">请求失败后的回调函数</param>
        /// <param name="id">唯一ID</param>
        /// <param name="options">选项</param>

        public static void StartAsyncWebRequest(string url, Action<ICsqWebResponse> success, Action<ICsqWebResponse> fail, int id, ServerConfig options = null)
        {
            var request = new CsqWebRequest(url);
            request.Options = options;
            ServerConfig.Apply(options, request);

            request.Id = id;
            request.Async = options == null ? true : options.Async;

            var mrEvent = request.GetAsync(success, fail);
            AsyncEvents.Add(mrEvent);
        }


        /// <summary>
        /// 等待异步请求完成
        /// </summary>
        /// <param name="millisecondsTimeout"></param>
        /// <returns></returns>
        public static bool WaitForAsyncEvents(int millisecondsTimeout = -1)
        {
            ManualResetEvent evt;
            int timeLeft = millisecondsTimeout;
            DateTime start = DateTime.Now;

            while (AsyncEvents.TryTake(out evt) && timeLeft != 0)
            {
                if (!evt.SafeWaitHandle.IsClosed)
                {
                    evt.WaitOne(timeLeft);
                    if (timeLeft >= 0)
                    {
                        //经过的时间等于所有线程等待时间
                        timeLeft = Math.Max(0, millisecondsTimeout - (int)(DateTime.Now - start).TotalMilliseconds);
                    }
                }
            }
            return timeLeft > 0;
        }

        /// <summary>
        /// 取消所有正在执行的异步请求
        /// </summary>

        public static void CancelAsyncEvents()
        {
            foreach (var evt in AsyncEvents)
            {
                evt.Close();
            }
        }


        /// <summary>
        /// 得到最后一个正在请求的唯一标识
        /// </summary>
        /// <returns></returns>
        private static int GetAsyncRequestID()
        {
            return Interlocked.Increment(ref LastAsyncRequestID);
        }

        #endregion
    }
}
